// Most game servers are written using Wincows IOCP, but I cannot complete IOCP server in 24 hours.
// So, I wrote a tcp socket server using WSAEventSelect I/O model.

#ifndef SOCKETSERVER_H
#define SOCKETSERVER_H

#include <assert.h>
#include <WinSock2.h>

#include "Packet.h"

//===========================================================================
static const unsigned int BUFFER_SIZE = 4096;

//===========================================================================
static const unsigned int MAX_CLIENTS = WSA_MAXIMUM_WAIT_EVENTS; /* 64 */

//===========================================================================
// Buffer
//===========================================================================
struct SocketBuffer
{
    WSABUF   buf;
    char     buffer[ BUFFER_SIZE ];
    unsigned size;

    SocketBuffer( void )
        : size( 0 )
    {
        this->buf.buf = this->buffer;
        this->buf.len = BUFFER_SIZE;
    }

    void Initialize( void ) {
        this->buf.buf = this->buffer;
        this->buf.len = BUFFER_SIZE;
        this->size    = 0;
    }

    bool Recv( unsigned int size ) {
        if( !( size <= BUFFER_SIZE - this->size ) )
            return false;

        this->size    += size;
        this->buf.buf += size;
        this->buf.len -= size;

        return true;
    }
    
    void Drain( unsigned int size ) {
        assert( size <= this->size );
        
        if( size == this->size )
            this->size = 0;
        else {
            ::memcpy_s(
                this->buffer,
                BUFFER_SIZE,
                this->buffer + size,
                this->size - size
            );
            this->size -= size;
        }

        this->buf.buf = this->buffer + this->size;
        this->buf.len = BUFFER_SIZE - this->size;
    }
};

//===========================================================================
// Socket
//===========================================================================
class Socket
{
public :

     Socket( void );
     Socket( const Socket & socket );
    ~Socket( void );

    void Initialize( SOCKET socket );

    inline SOCKET GetSocket( void ) const;
           void   Release( void );
           bool   Recv( void );
    inline void   Drain( unsigned size );
    inline const char * GetBuffer( void );
    inline unsigned int GetBufferSize( void );

private :

    SOCKET       m_socket;
    SocketBuffer m_buffer;
};

//===========================================================================
// Clients
//===========================================================================
class Clients
{
public :

    Clients( void );
    ~Clients( void );

    inline unsigned int GetCount( void ) const;
    inline const WSAEVENT * GetEvents( void ) const;
    inline Socket * GetSocket( unsigned int index );
    inline WSAEVENT * GetEvent( unsigned int index );

    int Generate( SOCKET socket, long events = 0 );
    void Release( unsigned int index );

private :

    WSAEVENT m_events[  WSA_MAXIMUM_WAIT_EVENTS ];
    Socket   m_sockets[ WSA_MAXIMUM_WAIT_EVENTS ];
    unsigned m_count;

    void Cleanup( void );
};

//===========================================================================
// SocketServer
//===========================================================================
class SocketServer
{
public :

     SocketServer( void );
    ~SocketServer( void );

    bool Initialize( unsigned int port );
    bool Run( void );
    inline bool IsRunning( void );
    void Stop( void );
    bool IsTimeToStop( void );

    inline Clients * GetClients( void );

    static DWORD WINAPI EventThreadProc( LPVOID param );

public :

    virtual bool OnConnected(    SOCKET client ) = 0;
    virtual void OnDisconnected( SOCKET client ) = 0;
    virtual bool OnRead(
              SOCKET     client,
        const char *     data,
              unsigned   size,
              unsigned & processed
    ) = 0;

private :

    unsigned m_port;
    HANDLE   m_thread;
    HANDLE   m_stop;

    Clients  m_clients;
};

#endif // SOCKETSERVER_H